package org.monster.context.core.communicator;

import org.monster.context.core.manager.StringManager;

import java.util.Stack;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * 通信器线程池
 *
 * @author Monster
 */
public class CommunicatorThreadPool {

    /**
     * 日志器
     */
    private static final Logger logger = Logger.getLogger(CommunicatorThreadPool.class.getName());
    /**
     * 字符串管理器
     */
    private static final StringManager sm = StringManager.getManager(Constants.Package);
    /**
     * 最大通讯器数
     */
    private int maxSize;
    /**
     * 通讯器线程双端队列
     */
    private Stack<CommunicatorThread> communicatorThreads;

    /**
     * 构造方法
     *
     * @param initSize 初始化通讯器数
     * @param maxSize  最大通讯器数
     */
    public CommunicatorThreadPool(int initSize, int maxSize) {
        setMaxSize(maxSize);
        setCommunicatorThreads(new Stack<>());
        reset(initSize);
        logger.log(Level.INFO,
                sm.getString("communicatorThreadPool.constructorInfo", initSize, maxSize));
    }

    /**
     * 设置 最大通讯器数
     *
     * @param maxSize 最大大小
     */
    public void setMaxSize(int maxSize) {
        this.maxSize = maxSize;
    }

    /**
     * 设置 通讯器双端队列
     *
     * @param communicatorThreads 通信器线程栈
     */
    public void setCommunicatorThreads(Stack<CommunicatorThread> communicatorThreads) {
        this.communicatorThreads = communicatorThreads;
    }

    /**
     * 压入 通信器线程
     * 将线程插入通讯器线程池双端队列前面
     *
     * @param communicatorThread 通讯器器线程
     */
    public void pushCommunicatorThread(CommunicatorThread communicatorThread) {
        communicatorThreads.push(communicatorThread);
    }

    /**
     * 重置 通讯器池
     *
     * @param newSize 新通讯器数
     */
    public void reset(int newSize) {
        int poolSize = communicatorThreads.size();
        //如果通信器池大于通信器线程池当前数量大小，并且小于最大通讯器线程数，则产生新的通讯器线程
        if (newSize > poolSize) {
            for (int i = poolSize; i < newSize && newSize < maxSize; i++) {
                communicatorThreads.add(newCommunicatorThread());
            }
        } else if (newSize < poolSize) {
            //如果通信器池小于通信器线程池当前数量大小，则设置多余的通信器为无效的
            while (poolSize > newSize) {
                CommunicatorThread communicatorThread = communicatorThreads.pop();
                communicatorThread.interrupt();
            }
        }
    }

    /**
     * 等待 闲置通讯器线程
     *
     * @throws InterruptedException 中断异常
     */
    protected void waitIdleCommunicatorThread() throws InterruptedException {
        Thread.sleep(5);
    }

    /**
     * 获取 闲置通信器线程
     *
     * @return 通信器线程
     * @throws InterruptedException 中断异常
     */
    public synchronized CommunicatorThread getIdleCommunicatorThread()
            throws InterruptedException {
        while (true) {
            //迭代通信器线程池的栈
            for (CommunicatorThread communicatorThread : communicatorThreads) {
                //如果通讯器线程是阻塞状态
                if (communicatorThread.isBlocked()) {
                    // 设置通讯器线程非阻塞状态，调用#CommunicatorThread.restart进入可运行状态
                    communicatorThread.setBlocked(false);
                    //返回闲置通讯器线程
                    return communicatorThread;
                }
            }
            //如果在上面，没有获取闲置通讯器线程
            //并且通信器线程池大小于最大通讯器线程数，创建新通信器线程放入通信器线程池中
            if (communicatorThreads.size() < maxSize) {
                return newCommunicatorThread();
            }
            //如果已达最大通讯器线程数，没法获取闲置线程，则等待。。。
            waitIdleCommunicatorThread();
        }
    }

    /**
     * 创建，新通讯器线程并执行
     * 放入通讯器线程池中
     *
     * @return 通讯器线程
     */
    private CommunicatorThread newCommunicatorThread() {
        CommunicatorThread communicatorThread = new CommunicatorThread(this);
        communicatorThread.start();
        communicatorThreads.push(communicatorThread);
        return communicatorThread;
    }


    /**
     * 执行通讯器线程
     *
     * @param communicator 通讯器
     * @throws InterruptedException 中断异常
     */
    public void startCommunicatorThread(Communicator communicator) throws InterruptedException {
        CommunicatorThread communicatorThread = getIdleCommunicatorThread();
        communicatorThread.restart(communicator);
    }
}
